#!/bin/bash
#
# This script is used to configure the ELKS kernel.
#
# It was inspired by the challenge in the original Configure script
# to ``do something better'', combined with the actual need to ``do
# something better'' because the old configure script wasn't flexible
# enough.
#
# Raymond Chen was the original author of Configure.
# Michael Elizabeth Chastain (mec@shout.net) is the current maintainer.
#
# 05 Jul 1993
#	- use IFS='@' to get around a bug in a pre-version of bash-1.13
#	  with an empty IFS.
#
# 03 Sep 1995 <storner@osiris.ping.dk>
#	- added support for tri-state answers, for selecting modules to
#	  compile.
#
# 18 Sep 1995 Bernhard Kaindl <bkaindl@ping.at>
#	- added dummy functions for use with a config.in modified for
#	  make menuconfig.
#
# 30 Nov 1995 Axel Boldt <boldt@math.ucsb.edu>
#	- added help text support
#
# 28 Dec 1995 Paul Gortmaker
#	- make tri_state functions collapse to boolean if module support
#	  is not enabled.
#
# 01 Feb 1996 Aaron Ucko <ucko@vax1.rockhurst.edu>
#	- fix int and hex to accept arbitrary ranges
#
# 15 Feb 1996 Dick Streefland <dicks@tasking.nl>
#	- report new configuration items and ask for a value even when
#	  doing a "make oldconfig"
#
# 20 Mar 1996 Tom Dyas <tdyas@eden.rutgers.edu>
#	- when the module option is chosen for an item, define the macro
#	  <option_name>_MODULE
#
# 09 Mar 1997 Axel Boldt <boldt@math.ucsb.edu>
#	- avoid ? and + in regular expressions as GNU expr since version
#	  1.15 and up use \? and \+.
#
# 30 Mar 1997 Phil Blundell <pjb27@cam.ac.uk>
#	- added support for min/max arguments to "int"
#	- allow dep_tristate to take a list of dependencies rather than
#	  just one.
#
# 03 Sep 1998 Axel Boldt <boldt@math.ucsb.edu>
#	- allow for empty lines in help texts.
#
# 25 Oct 1998 Michael Elizabeth Chastain <mec@shout.net>
#	- put temporary files in current directory, not in /tmp.
#
# 24 Jan 1999 Michael Elizabeth Chastain, <mec@shout.net>
#	- Improve the exit message (Jeff Ronne).
#
# 24 Feb 2002 Riley Williams <rhw@memalpha.cx>
#	- Reformatted for readability.
#
# Make sure we're really running bash.
#
# I would really have preferred to write this script in a language with
# better string handling, but alas, bash is the only scripting language
# that I can be reasonable sure everybody has on their linux machine.

[ -z "$BASH" ] && { echo "Configure requires bash" 1>&2; exit 1; }

# Disable filename globbing once and for all.
# Enable function cacheing.

set -f -h

# Dummy functions for use with a config.in modified for menuconf

function mainmenu_option () {
	:
}
function mainmenu_name () {
	:
}
function endmenu () {
	:
}

# gethelp prints to stdout the corresponding help text from Configure.help
# for the specified variable.
#
#       gethelp variable

function gethelp () {
    #first escape regexp special characters in the argument:
    var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
    #now pick out the right help text:
    text=$(sed -n "/^$var[ 	]*\$/,\${
			/^$var[ 	]*\$/c\\
${var}:\\

			/^#/b
			/^[^ 	]/q
			p
		    }" config/Configure.help)
    if [ -z "$text" ]; then
	echo "  Sorry, no help available for this option yet."
    else
	echo "$text"
    fi
}

# helpvar prints to stdout the corresponding help text from Configure.help
# for one or more specified variables.
#
#	helpvar variable...

function helpvar () {
    local PARM

    echo
    if [ -f config/Configure.help ]; then
	for PARM do
	    gethelp "$PARM"
	    echo
	done
    else
	echo "  Can't access the file Configure.help which"
	echo "  should contain the help texts."
	echo
    fi
}

# help prints to stdout the corresponding help texts from Configure.help
# for its parameters.
#
#	help variable...

function help () {
    helpvar "$@" | ${PAGER:-more}
}

#
# readln reads a line into $ans.
#
#	readln prompt default oldval
#
function readln () {
    if [ -n "$DEFAULT" -a -n "$3" ]; then
	echo "$1"
	ans=$2
    else
	echo -n "$1"
	[ -z "$3" ] && echo -n "(NEW) "
	IFS='@' read ans || exit 1
	[ -z "$ans" ] && ans=$2
    fi
}

#
# comment does some pretty-printing
#
#	comment 'xxx'
# 
function comment () {
    local S=`echo "$1" | tr -s '\t' ' ' | sed 's/^ //'`

    echo " *"
    echo " * $S"
    echo " *"
    (echo "" ; echo "#"; echo "# $S" ; echo "#") >>$CONFIG
    (echo "" ; echo "/*"; echo " * $S" ; echo " */") >>$CFG_H
}

# define_bool sets the value of a boolean argument
#
#	define_bool define value

function define_bool () {
    define_tristate $1 $2
}

# define_busyelks sets the value of a busyelks argument
#
#	define_busyelks define value

function define_busyelks () {
    case "$2" in
	"y")	echo "$1=y" >>$CONFIG
		echo "#define $1 1" >>$CFG_H
		;;
	"b")	if [ "$CONFIG_APP_BUSYELKS" = "y" ]; then
		    echo "$1=b" 		>> $CONFIG
		    echo "#undef $1"		>> $CFG_H
		    echo "#define $1_BUSYELKS 1"	>> $CFG_H
		else
		    echo "$1=y" 		>> $CONFIG
		    echo "#define $1 1" 	>> $CFG_H
		fi
		;;
	"n")	echo "# $1 is not set" >>$CONFIG
		echo "#undef  $1" >>$CFG_H
		;;
    esac
    eval "$1=$2"
}


# define_tristate sets the value of a tristate argument
#
#	define_tristate define value

function define_tristate () {
    case "$2" in
	"y")	echo "$1=y" >>$CONFIG
		echo "#define $1 1" >>$CFG_H
		;;
	"m")	echo "$1=m" >>$CONFIG
		echo "#undef  $1" >>$CFG_H
		echo "#define $1_MODULE 1" >>$CFG_H
		;;
	"n")	echo "# $1 is not set" >>$CONFIG
		echo "#undef  $1" >>$CFG_H
		;;
    esac
    eval "$1=$2"
}

# bool processes a boolean argument
#
#	bool question define [default]

function bool () {
    old=$(eval echo "\${$2}")
    if [ $# -eq 3 ]; then
	def=${old:-$3}
    else
	def=${old:-n}
    fi
    case "$def" in
	"y"|"m"|"b")	defprompt="Y/n/?"
			def="y"
			;;
	"n")		defprompt="N/y/?"
			;;
    esac
    while true; do
	readln "$1 ($2) [$defprompt] " "$def" "$old"
	case "$ans" in
	    [yY] | [yY]es )	define_bool "$2" "y"
				break
				;;
	    [nN] | [nN]o )	define_bool "$2" "n"
				break
				;;
	    * )			help "$2"
				;;
	esac
    done
}

# busyelks processes a busyelks argument
#
#	busyelks question define

function busyelks () {
    if [ "$CONFIG_APP_BUSYELKS" != "y" ]; then
	bool "$1" "$2"
    else
	old=$(eval echo "\${$2}")
	def=${old:-'n'}
	case "$def" in
	    "y")	defprompt="Y/b/n/?"
			;;
	    "b")	defprompt="B/n/y/?"
			;;
	    "n")	defprompt="N/y/b/?"
			;;
	esac
	while true; do
	    readln "$1 ($2) [$defprompt] " "$def" "$old"
	    case "$ans" in
		[yY] | [yY]es )		define_busyelks "$2" "y"
					break
					;;
		[nN] | [nN]o )		define_busyelks "$2" "n"
					break
					;;
		[bB] | [bB]usyelks )	define_busyelks "$2" "b"
					break
					;;
		* )			help "$2"
					;;
	    esac
	done
    fi
}

# dep_busyelks_notval writes out an error message used by dep_busyelks, and
# is not used elsewhere.
#
#	dep_busyelks_notval

function dep_busyelks_notval () {
    echo
    echo "  This answer is not allowed, because it is not consistent with"
    echo "  your other choices."
    echo
    echo "  This command depends on another one which you chose to compile"
    echo "  as a busyelks command. This means that you can either compile this one"
    echo "  as a busyelks command as well (with B) or leave it out altogether (N)."
    echo
}

# tristate processes a tristate argument
#
#	tristate question define

function tristate () {
    if [ "$CONFIG_MODULES" != "y" ]; then
	bool "$1" "$2"
    else
	old=$(eval echo "\${$2}")
	def=${old:-'n'}
	case "$def" in
	    "y")	defprompt="Y/m/n/?"
			;;
	    "m")	defprompt="M/n/y/?"
			;;
	    "n")	defprompt="N/y/m/?"
			;;
	esac
	while true; do
	    readln "$1 ($2) [$defprompt] " "$def" "$old"
	    case "$ans" in
		[yY] | [yY]es )		define_tristate "$2" "y"
					break
					;;
		[nN] | [nN]o )		define_tristate "$2" "n"
					break
					;;
		[mM] | [mM]odule)	define_tristate "$2" "m"
					break
					;;
		* )			help "$2"
					;;
	    esac
	done
    fi
}

# dep_tristate_notval writes out an error message used by dep_tristate, and
# is not used elsewhere.
#
#	dep_tristate_notval

function dep_tristate_notval () {
    echo
    echo "  This answer is not allowed, because it is not consistent with"
    echo "  your other choices."
    echo
    echo "  This driver depends on another one which you chose to compile"
    echo "  as a module. This means that you can either compile this one"
    echo "  as a module as well (with M) or leave it out altogether (N)."
    echo
}

# dep_tristate processes a tristate argument that depends upon another option
# or options. If any of the options we depend upon is a module, then the only
# allowable options are M or N. If all are Y, then this is a normal tristate.
# This is used in cases where modules are nested, and one module requires the
# presence of something else in the kernel.
#
#	dep_tristate question define default ...

function dep_tristate () {
    old=$(eval echo "\${$2}")
    def=${old:-'n'}
    ques=$1
    var=$2
    need_module=0
    shift 2
    while [ $# -gt 0 ]; do
	case "$1" in
	    n)	define_tristate "$var" "n"
		return
		;;
	    m)	need_module=1
		;;
	esac
	shift
    done
    if [ $need_module = 1 ]; then
	if [ "$CONFIG_MODULES" = "y" ]; then
	    case "$def" in
		"y" | "m")	defprompt="M/n/?"
				def="m"
				;;
		"n")		defprompt="N/m/?"
				;;
	    esac
	    while true; do
		readln "$ques ($var) [$defprompt] " "$def" "$old"
		case "$ans" in
		    [nN] | [nN]o )	define_tristate "$var" "n"
					break
					;;
		    [mM] | [mM]odule )	define_tristate "$var" "m"
					break
					;;
		    [yY] | [yY]es ) dep_tristate_notval
					;;
		    * ) 		help "$var"
					;;
		esac
	    done
	fi
    else
	tristate "$ques" "$var"
    fi
}

# dep_bool processes a tristate argument that depends upon another option or
# options. Unless all of those options are "y" this option is set to "n".
#
#	dep_bool question define ...

function dep_bool () {
    ques=$1
    var=$2
    shift 2
    while [ $# -gt 0 ]; do
	case "$1" in
	    m | n)	define_bool "$var" "n"
			return
			;;
	esac
	shift
    done
    bool "$ques" "$var"
}

# dep_mbool processes a tristate argument that depends upon another option or
# options. If any of those options are "n" this option is set to "n".
#
#	dep_mbool question define ...

function dep_mbool () {
    ques=$1
    var=$2
    shift 2
    while [ $# -gt 0 ]; do
	case "$1" in
	    n)	define_bool "$var" "n"
		return
		;;
	esac
	shift
    done
    bool "$ques" "$var"
}

# define_int sets the value of a integer argument
#
#	define_int define value

function define_int () {
    echo "$1=$2" >>$CONFIG
    echo "#define $1 ($2)" >>$CFG_H
    eval "$1=$2"
}

# int processes an integer argument with optional limits
#
#	int question define default [min [max]]

function int () {
    old=$(eval echo "\${$2}")
    def=${old:-$3}
    if [ $# -gt 3 ]; then
	min=$4
    else
	min=-10000000    # !!
    fi
    if [ $# -gt 4 ]; then
	max=$5
    else
	max=10000000     # !!
    fi
    while true; do
	readln "$1 ($2) [$def] " "$def" "$old"
	if expr \( \( $ans + 0 \) \>= $min \) \& \( $ans \<= $max \) >/dev/null 2>&1 ; then
	    define_int "$2" "$ans"
	    break
	else
	    help "$2"
	fi
    done
}

# define_hex sets the value of a hexadecimal argument
#
#	define_hex define value

function define_hex () {
    echo "$1=$2" >>$CONFIG
    echo "#define $1 0x${2#*[x,X]}" >>$CFG_H
    eval "$1=$2"
}

# hex processes an hexadecimal argument
#
#	hex question define default

function hex () {
    old=$(eval echo "\${$2}")
    def=${old:-$3}
    def=${def#*[x,X]}
    while true; do
	readln "$1 ($2) [$def] " "$def" "$old"
	ans=${ans#*[x,X]}
	if expr "$ans" : '[0-9a-fA-F][0-9a-fA-F]*$' > /dev/null; then
	    define_hex "$2" "$ans"
	    break
	else
	    help "$2"
	fi
    done
}

# define_string sets the value of a string argument
#
#	define_string define value

function define_string () {
    if [ "$2" != "" ]; then
        echo "$1=\"$2\"" >>$CONFIG
        echo "#define $1 \"$2\"" >>$CFG_H
    else
        #echo "# $1 is not set" >>$CONFIG
        echo "#undef  $1" >>$CFG_H
    fi
    eval "$1=\"$2\""
}

# string processes a string argument
#
#	string question define default

function string () {
    old=$(eval echo "\${$2}")
    def=${old:-$3}
    while true; do
	if [ "$old" = "?" ]; then
	    readln "$1 ($2) [$def] " "$def" ""
	else
	    readln "$1 ($2) [$def] " "$def" "$old"
	fi
	if [ "$ans" = "?" ]; then
	    help "$2"
	else
	    break
	fi
    done
    define_string "$2" "$ans"
}

# choice processes a choice list (1-out-of-n)
#
#	choice question choice-list default
#
# The choice list has a syntax of:
#
#	NAME WHITESPACE VALUE { WHITESPACE NAME WHITESPACE VALUE }
#
# The user may enter any unique prefix of one of the NAMEs and
# choice will define VALUE as if it were a boolean option.
# VALUE must be in all uppercase.  Normally, VALUE is of the
# form CONFIG_<something>.  Thus, if the user selects <something>,
# the CPP symbol CONFIG_<something> will be defined and the
# shell variable CONFIG_<something> will be set to "y".

function choice () {
    question="$1"
    choices="$2"
    old=
    def=$3

    # determine default answer:
    names=""
    set -- $choices
    firstvar=$2
    while [ -n "$2" ]; do
	if [ -n "$names" ]; then
	    names="$names, $1"
	else
	    names="$1"
	fi
	if [ "$(eval echo \"\${$2}\")" = "y" ]; then
	    old=$1
	    def=$1
	fi
	shift 2
    done
    val=""
    while [ -z "$val" ]; do
	ambg=n
	readln "$question ($names) [$def] " "$def" "$old"
	ans=$(echo $ans | tr a-z A-Z)
	set -- $choices
	while [ -n "$1" ]; do
	    name=$(echo $1 | tr a-z A-Z)
	    case "$name" in
		"$ans"* | */"$ans"* )
			case "$name" in
			    "$ans" | */"$ans"/* | "$ans"/* | */"$ans" )
					val="$2"
					break		# exact match
					;;
			esac
			if [ -n "$val" ]; then
			    echo
			    echo "  Sorry, \"$ans\" is ambiguous; please enter a longer string."
			    echo
			    val=""
			    ambg=y
			    break
			else
			    val="$2"
			fi
			;;
	    esac
	    shift 2
	done
	if [ "$val" = "" -a "$ambg" = "n" ]; then
	    help "$firstvar"
	fi
    done
    set -- $choices
    while [ -n "$2" ]; do
	if [ "$2" = "$val" ]; then
	    echo "  defined $val"
	    define_bool "$2" "y"
	else
	    define_bool "$2" "n"
	fi
	shift 2
    done
}

CONFIG=.tmpconfig
CFG_H=.tmpconfig.h
trap "rm -f $CONFIG $CFG_H ; exit 1" 1 2

# Make sure we start out with a clean slate.

cat > $CONFIG <<-.EOF
	#
	# Automatically generated make config: don't edit
	#
	.EOF
cat > $CFG_H <<-.EOF
	/*
	 * Automatically generated C config: don't edit
	 */
	#define AUTOCONF_INCLUDED
	.EOF
DEFAULT=""
if [ "$1" = "-d" ] ; then
    DEFAULT="-d"
    shift
fi
if [ "$1" = "-D" ] ; then	# only update autoconf.h  -- tkchia 20200310
    DEFAULT="-D"
    shift
fi
CFG_IN=./config.in
if [ "$1" != "" ] ; then
    CFG_IN=$1
fi
DEFAULTS=elks/arch/i86/defconfig
if [ -f .config ]; then
    DEFAULTS=.config
fi
if [ -f $DEFAULTS ]; then
    cat <<-.EOF
	#
	# Using defaults found in $DEFAULTS
	#
	.EOF
    . $DEFAULTS
    sed -e 's/# \(CONFIG_[^ ]*\) is not.*/\1=n/' < $DEFAULTS > .config-is-not.$$
    . .config-is-not.$$
    rm .config-is-not.$$
else
    cat <<-.EOF
   	#
	# No defaults found.
	#
	.EOF
fi
. $CFG_IN
rm -f .config.old
if [ "$DEFAULT" = -D ]; then
    rm .tmpconfig
else
    if [ -f .config ]; then
	mv .config .config.old
    fi
    mv .tmpconfig .config
fi
mv .tmpconfig.h include/autoconf.h
echo
exit 0
